/* MikroPictures demo */

#include <stdlib.h>
#include <math.h>

#include "p24FJ256GB110.h"	// PIC24 register and bit definitions
#include "MikroPictures.h"	// Picture display functions
#include "Mikro.h"		// LCD variables, functions, macros

/****** Configuration selections **************************************/
_CONFIG1(JTAGEN_OFF & GWRP_OFF & FWDTEN_OFF & ICS_PGx2);
_CONFIG2(PLLDIV_DIV2 & POSCMOD_HS & FNOSC_PRIPLL & IOL1WAY_OFF);

#define CC_X 160		/* clock center x */
#define CC_Y 120		/* clock center y */
#define CLOCK_SIDELENGTH 150	/* size of clock image */
#define HANDLENGTH (CLOCK_SIDELENGTH / 4 - 5)	/* clock hand length */

#define LOOPS_PER_SEC 30
#define SECONDS_PER_MINUTE 60

#define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089

void Initial();
void DrawLine(uint8 x0, uint8 y0, uint8 x1, uint8 y1, uint16 color);
static void SetupTrigTables(uint16 N);
static void UpdateHand(void);
static void HandLimits(uint16 * X0, uint16 * X1, uint16 * Y0, uint16 * Y1,
		       uint8 sec);

/* trig lookup tables */
float sin_LUT[SECONDS_PER_MINUTE];
float cos_LUT[SECONDS_PER_MINUTE];

int main()
{
	uint16 timer_modulo = 0;
	Initial();
	DrawPicture(1, 1, "opensource.mikropic");
	DrawPicture(CC_X - CLOCK_SIDELENGTH / 2,
		    CC_Y - CLOCK_SIDELENGTH / 2, "clockface.mikropic");

	while (1) {
		while (!_T5IF);
		if (++timer_modulo > LOOPS_PER_SEC) {
			timer_modulo = 0;
			UpdateHand();
		}
		_T5IF = 0;
	}

	return 0;
}

void Initial()
{
	AD1PCFGL = 0xFFFF;	// Make all ADC pins default to digital pins
	PMP_Init();		// Configure PMP module for LCD
	LCD_Init();		// Configure LCD controller
	DrawRectangle(0, 0, 319, 239, WHITE);
	_TRISA3 = 0;		// Make RA3 an output (pin 50 of Mikro board)
	_TRISD0 = 0;		// digital
	TMR5 = 0;		// Clear Timer5
	PR5 = 19999;		// Set period of Timer5 to 10 ms
	T5CON = 0x8010;		// Clock Timer5 with Fcy/8 = 2 MHz

	SetupTrigTables(SECONDS_PER_MINUTE);
}

/* Bresenham's Line Drawing Algorithm */
void DrawLine(uint8 x0, uint8 y0, uint8 x1, uint8 y1, uint16 color)
{
	sint16 err, e2, sx, sy;
	sint16 dx = abs(x1 - x0);
	sint16 dy = abs(y1 - y0);

	if (x0 < x1) {
		sx = 1;
	} else {
		sx = -1;
	}
	if (y0 < y1) {
		sy = 1;
	} else {
		sy = -1;
	}
	err = dx - dy;

	_LATF12 = 0;		// Display CS Enable;
	while (1) {
		WritePixelAt(x0, y0, color);
		if (x0 == x1 && y0 == y1) {
			break;
		}
		e2 = 2 * err;
		if (e2 > -dy) {
			err -= dy;
			x0 += sx;
		}
		if (e2 < dx) {
			err += dx;
			y0 += sy;
		}
	}
	_LATF12 = 1;		// Display CS Disable;
}

/* erase current hand and draw new one */
static void UpdateHand(void)
{
	static uint8 sec = 0;
	uint16 X0, X1, Y0, Y1;

	HandLimits(&X0, &X1, &Y0, &Y1, sec);
	DrawLine(X0, Y0, X1, Y1, WHITE);

	sec++;
	if (sec >= 60) {
		sec = 0;
	}

	HandLimits(&X0, &X1, &Y0, &Y1, sec);
	DrawLine(X0, Y0, X1, Y1, BLACK);

}

/* calculate clock hand endpoints */
static void HandLimits(uint16 * X0, uint16 * X1, uint16 * Y0, uint16 * Y1,
		       uint8 sec)
{
	*X0 = CC_X;
	*X1 = CC_X + (sint16) (cos_LUT[sec] * (float) HANDLENGTH);
	*Y0 = CC_Y;
	*Y1 = CC_Y + (sint16) (sin_LUT[sec] * (float) HANDLENGTH);
}

/* 
 * Set up an N-entry lookup table for each of sine and cosine, since calculating them in 
 * real time is pretty slow
 */
void SetupTrigTables(uint16 N)
{
	int i;
	for (i = 0; i < N; i++) {
		sin_LUT[i] = sin((float) i * 2 * PI / (float) N);
		cos_LUT[i] = cos((float) i * 2 * PI / (float) N);
	}
}
